/*
 * Created on 2004-12-7
 */
package com.tss.util;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Savepoint;
import java.sql.Statement;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.Map;
import java.util.Properties;

/**
 * @author DaiRongBang
 *
 * ��ݿ�t�ӳض���
 */
public class DbConnectionPool extends DbConnectionProvider {
	private static final String NAME = "Tss Connection Pool";
	private static final String DESCRIPTION = "The Tss connection provider "
		+ "that uses the connection pool from tss.com. It works with "
		+ "almost any database setup, is customizable, and offers good performance. "
		+ "Use this connection provider unless you have your own or can use a "
		+ "container managed connection pool.";
	private static final String AUTHOR = "www.tss.com.cn";
	private static final int MAJOR_VERSION = 1;
	private static final int MINOR_VERSION = 0;
	private static final boolean POOLED = true;

	private ConnectionPool connectionPool = null;
	private Properties props;
	private Properties propDescriptions;

	private Object initLock = new Object();

	public DbConnectionPool() {
		//this.manager = manager;
		props = new Properties();
		propDescriptions = new Properties();
		//Initialize all property values
		initializeProperties();
		//Load any existing property values
		loadProperties();
	}
	
	/**
	 * Returns a database connection.
	 */
	public Connection getConnection() {
		if (connectionPool == null) {
			//block until the init has been done
			synchronized(initLock) {
				//if still null, something has gone wrong
				if (connectionPool == null) {
					System.err.println("Warning: DbConnectionDefaultPool.getConnection() was " +
					"called when the internal pool has not been initialized.");
					return null;
				}
			}
		}
		return new ConnectionWrapper(connectionPool.getConnection(), connectionPool);
	}

	/**
	 * Starts the pool.
	 */
	protected void start() {
		//acquire lock so that no connections can be returned.
		synchronized (initLock) {
			//Get properties
			String driver = props.getProperty("driver");
			String connectstring = props.getProperty("connectstring");
			String username = props.getProperty("username");
			String password = props.getProperty("password");
			int minConnections = 0, maxConnections = 10;
			double connectionTimeout = 0.0;
			try {
				minConnections = Integer.parseInt(props.getProperty("minConnections"));
				maxConnections = Integer.parseInt(props.getProperty("maxConnections"));
				connectionTimeout = Double.parseDouble(props.getProperty("connectionTimeout"));
			}
			catch (Exception e) {
				System.err.println("Error: could not parse default pool properties. " +
					"Make sure the values exist and are correct.");
				e.printStackTrace();
				return;
			}
			String logPath = props.getProperty("logPath");

			try {
				connectionPool = new ConnectionPool(driver, connectstring, username, password,
					minConnections, maxConnections, logPath, connectionTimeout);
			}
			catch (IOException ioe) {
				System.err.println("Error starting DbConnectionDefaultPool: " + ioe);
				ioe.printStackTrace();
			}
		}
	}

	/**
	 * Restarts the pool to take into account any property changes.
	 */
	protected void restart() {
		//Kill off pool.
		destroy();
		//Reload properties.
		loadProperties();
		//Start a new pool.
		start();
	}

	/**
	 * Destroys the connection pool.
	 */
	protected void destroy() {
		if (connectionPool != null) {
			try {
				connectionPool.destroy(1);
			}
			catch (Exception e) {
				e.printStackTrace();
			}
		}
		//Release reference to connectionPool
		connectionPool = null;
	}

	/**
	 * Returns the value of a property of the connection provider.
	 *
	 * @param name the name of the property.
	 * @returns the value of the property.
	 */
	public String getProperty(String name) {
		return (String)props.get(name);
	}

	/**
	 * Returns the description of a property of the connection provider.
	 *
	 * @param name the name of the property.
	 * @return the description of the property.
	 */
	public String getPropertyDescription(String name) {
		return (String)propDescriptions.get(name);
	}

	/**
	 * Returns an enumeration of the property names for the connection provider.
	 */
	public Enumeration propertyNames() {
		return props.propertyNames();
	}

	
	/**
	 * Give default values to all the properties and descriptions.
	 */
	private void initializeProperties() {
		props.put("driver","");
		props.put("connectstring","");
		props.put("username","");
		props.put("password","");
		props.put("minConnections","");
		props.put("maxConnections","");
		props.put("logPath","");
		props.put("connectionTimeout","");

		propDescriptions.put("driver","JDBC driver. e.g. 'com.mysql.jdbc.Driver'");
		propDescriptions.put("connectstring","JDBC connect string. e.g. 'jdbc:mysql://localhost:3306/databasename?characterEncoding=gb2312'");
		propDescriptions.put("username","Database username. e.g. 'root'");
		propDescriptions.put("password","Database password. e.g. 'root'");
		propDescriptions.put("minConnections","Minimum # of connections to start with in pool. Three is the recommended minimum");
		propDescriptions.put("maxConnections","Maximum # of connections in dynamic pool. Fifteen should give good performance for an average load.");
		propDescriptions.put("logPath","Absolute path name for log file. e.g. 'c:\\logs\\sysDbLog.log'");
		propDescriptions.put("connectionTimeout","Time in days between connection resets. e.g. '.5'");
	}
	
	/**
	 * Load whatever properties that already exist.
	 */
	private void loadProperties() {
		String driver = PropertyManager.getProperty("DbConnectionPool.driver");
		String connectstring = PropertyManager.getProperty("DbConnectionPool.connectstring");
		String username = PropertyManager.getProperty("DbConnectionPool.username");
		String password = PropertyManager.getProperty("DbConnectionPool.password");
		String minConnections = PropertyManager.getProperty("DbConnectionPool.minConnections");
		String maxConnections = PropertyManager.getProperty("DbConnectionPool.maxConnections");
		String logPath = PropertyManager.getProperty("DbConnectionPool.logPath");
		String connectionTimeout = PropertyManager.getProperty("DbConnectionPool.connectionTimeout");

		if (driver != null) {  props.setProperty("driver", driver);  }
		if (connectstring != null) {  props.setProperty("connectstring", connectstring);  }
		if (username != null) {  props.setProperty("username", username);  }
		if (password != null) {  props.setProperty("password", password);  }
		if (minConnections != null) {  props.setProperty("minConnections", minConnections);  }
		if (maxConnections != null) {  props.setProperty("maxConnections", maxConnections);  }
		if (logPath != null) {  props.setProperty("logPath", logPath);  }
		if (connectionTimeout != null) {  props.setProperty("connectionTimeout", connectionTimeout);  }
	}
	

	private class ConnectionPool implements Runnable {
		private Thread runner;

		private Connection[] connPool;
		private int[] connStatus;

		private long[] connLockTime, connCreateDate;
		private String[] connID;
		private String dbDriver, dbConnectstring, dbLogin, dbPassword, logFileString;
		private int currConnections, connLast, minConns, maxConns, maxConnMSec;

		//available: set to false on destroy, checked by getConnection()
		private boolean available=true;

		private PrintWriter log;
		private SQLWarning currSQLWarning;
		private String pid;

		/**
		 * Creates a new Connection Broker<br>
		 * dbDriver:        JDBC driver. e.g. 'oracle.jdbc.driver.OracleDriver'<br>
		 * dbConnectstring:        JDBC connect string. e.g. 'jdbc:oracle:thin:@203.92.21.109:1526:orcl'<br>
		 * dbLogin:         Database login name.  e.g. 'Scott'<br>
		 * dbPassword:      Database password.    e.g. 'Tiger'<br>
		 * minConns:        Minimum number of connections to start with.<br>
		 * maxConns:        Maximum number of connections in dynamic pool.<br>
		 * logFileString:   Absolute path name for log file. e.g. 'c:\temp\mylog.log' <br>
		 * maxConnTime:     Time in days between connection resets. (Reset does a basic cleanup)<br>
		 */
		public ConnectionPool (String dbDriver, String dbConnectstring, String dbLogin,
				String dbPassword, int minConns, int maxConns,
					String logFileString, double maxConnTime) throws IOException
		{
			connPool = new Connection[maxConns];
			connStatus = new int[maxConns];
			connLockTime = new long[maxConns];
			connCreateDate = new long[maxConns];
			connID = new String[maxConns];
			currConnections = minConns;
			this.maxConns = maxConns;
			this.dbDriver = dbDriver;
			this.dbConnectstring = dbConnectstring;
			this.dbLogin = dbLogin;
			this.dbPassword = dbPassword;
			this.logFileString = logFileString;
			maxConnMSec = (int)(maxConnTime * 86400000.0);  //86400 sec/day
			if(maxConnMSec < 30000) {  // Recycle no less than 30 seconds.
				maxConnMSec = 30000;
			}

			try {
				log = new PrintWriter(new FileOutputStream(logFileString),true);
				// Can't open the requested file. Open the default file.
			}
			catch (IOException e1) {
				System.err.println("Warning: DbConnectionPool could not open \""
					+ logFileString + "\" to write log to. Make sure that your Java " +
					"process has permission to write to the file and that the directory exists."
				);
				try {
					log = new PrintWriter(new FileOutputStream("DCB_" +
						System.currentTimeMillis() + ".log"), true
					);
				}
				catch (IOException e2) {
					throw new IOException("Can't open any log file");
				}
			}

			// Write the pid file (used to clean up dead/broken connection)
			SimpleDateFormat formatter
				= new SimpleDateFormat ("yyyy.MM.dd G 'at' hh:mm:ss a zzz");
			java.util.Date nowc = new java.util.Date();
			pid = formatter.format(nowc);

			BufferedWriter pidout = new BufferedWriter(new
									FileWriter(logFileString + "pid"));
			pidout.write(pid);
			pidout.close();

			log.println("Starting ConnectionPool:");
			log.println("dbDriver = " + dbDriver);
			log.println("dbConnectstring = " + dbConnectstring);
			log.println("dbLogin = " + dbLogin);
			log.println("log file = " + logFileString);
			log.println("minconnections = " + minConns);
			log.println("maxconnections = " + maxConns);
			log.println("Total refresh interval = " + maxConnTime + " days");
			log.println("-----------------------------------------");


			// Initialize the pool of connections with the mininum connections:
			// Problems creating connections may be caused during reboot when the
			//    servlet is started before the database is ready.  Handle this
			//    by waiting and trying again.  The loop allows 5 minutes for
			//    db reboot.
			boolean connectionsSucceeded=false;
			int dbLoop=20;

			try {
				for(int i=1; i < dbLoop; i++) {
					try {
						for(int j=0; j < currConnections; j++) {
							createConn(j);
						}
						connectionsSucceeded=true;
						break;
					}
					catch (SQLException e){
						log.println("--->Attempt (" + String.valueOf(i) +
								" of " + String.valueOf(dbLoop) +
								") failed to create new connections set at startup: ");
						log.println("    " + e);
						log.println("    Will try again in 15 seconds...");
						try { Thread.sleep(15000); }
						catch(InterruptedException e1) {}
					}
				}
				if(!connectionsSucceeded) { // All attempts at connecting to db exhausted
					log.println("\r\nAll attempts at connecting to Database exhausted");
					throw new IOException();
				}
			}
			catch (Exception e) {
				e.printStackTrace();
				throw new IOException();
			}

			// Fire up the background housekeeping thread

			runner = new Thread(this);
			runner.start();

		} //End ConnectionPool()


		/**
		 * Housekeeping thread.  Runs in the background with low CPU overhead.
		 * Connections are checked for warnings and closure and are periodically
		 * restarted.
		 * This thread is a catchall for corrupted
		 * connections and prevents the buildup of open cursors. (Open cursors
		 * result when the application fails to close a Statement).
		 * This method acts as fault tolerance for bad connection/statement programming.
		 */
		public void run() {
			boolean forever = true;
			Statement stmt=null;
			String currCatalog=null;

			while(forever) {
				// Make sure the log file is the one this instance opened
				// If not, clean it up!
				try {
					BufferedReader in = new BufferedReader(new FileReader(logFileString + "pid"));
					String curr_pid = in.readLine();
					if(curr_pid.equals(pid)) {
						//log.println("They match = " + curr_pid);
					}
					else {
						//log.println("No match = " + curr_pid);
						log.close();

						 // Close all connections silently - they are definitely dead.
						for(int i=0; i < currConnections; i++) {
							try {
								connPool[i].close();
							}
							catch (SQLException e1) {} // ignore
						}
						// Returning from the run() method kills the thread
						return;
					}
					in.close();
				}
				catch (IOException e1) {
					log.println("Can't read the file for pid info: " +
						logFileString + "pid");
				}

				// Get any Warnings on connections and print to event file
				for(int i=0; i < currConnections; i++) {
					try {
						currSQLWarning = connPool[i].getWarnings();
						if(currSQLWarning != null) {
							log.println("Warnings on connection " +
								String.valueOf(i) + " " + currSQLWarning);
							connPool[i].clearWarnings();
						}
					}
					catch(SQLException e) {
						log.println("Cannot access Warnings: " + e);
					}
				}

				for(int i=0; i < currConnections; i++) { // Do for each connection
					long age = System.currentTimeMillis() - connCreateDate[i];

					synchronized(connStatus) {
						if(connStatus[i] > 0) { // In use, catch it next time!
							continue;
						}
						connStatus[i] = 2; // Take offline (2 indicates housekeeping lock)
					}

					try {  // Test the connection with createStatement call
						if(age > maxConnMSec) {  // Force a reset at the max conn time
							throw new SQLException();
						}

						stmt = connPool[i].createStatement();
						connStatus[i] = 0;  // Connection is O.K.
						//log.println("Connection confirmed for conn = " +
						//             String.valueOf(i));

						// Some DBs return an object even if DB is shut down
						if(connPool[i].isClosed()) {
							throw new SQLException();
						}
						// Connection has a problem, restart it
					}
					catch(SQLException e) {
						try {
							log.println(new Date().toString() +
								" ***** Recycling connection " +
								String.valueOf(i) + ":"
							);

							connPool[i].close();
							createConn(i);
						}
						catch(SQLException e1) {
							log.println("Failed: " + e1);
							connStatus[i] = 0;  // Can't open, try again next time
						}
					}
					finally {
						try {
							if(stmt != null) {
								stmt.close();
							}
						}
						catch(SQLException e1){};
					}
				}

				try {
					Thread.sleep(20000);
				}  // Wait 20 seconds for next cycle
				catch(InterruptedException e) {
					// Returning from the run method sets the internal
					// flag referenced by Thread.isAlive() to false.
					// This is required because we don't use stop() to
					// shutdown this thread.
					return;
				}
			}
		} // End run

		/**
		 * This method hands out the connections in round-robin order.
		 * This prevents a faulty connection from locking
		 * up an application entirely.  A browser 'refresh' will
		 * get the next connection while the faulty
		 * connection is cleaned up by the housekeeping thread.
		 *
		 * If the min number of threads are ever exhausted, new
		 * threads are added up the the max thread count.
		 * Finally, if all threads are in use, this method waits
		 * 2 seconds and tries again, up to ten times.  After that, it
		 * returns a null.
		 */
		public Connection getConnection() {

			Connection conn=null;

			if(available){
				boolean gotOne = false;

				for(int outerloop=1; outerloop<=10; outerloop++) {

					try  {
						int loop=0;
						int roundRobin = connLast + 1;
						if(roundRobin >= currConnections) roundRobin=0;

						do {
							synchronized(connStatus) {
								if((connStatus[roundRobin] < 1) &&
													(! connPool[roundRobin].isClosed()))
								{
									conn = connPool[roundRobin];
									connStatus[roundRobin]=1;
									connLockTime[roundRobin] =
															System.currentTimeMillis();
									connLast = roundRobin;
									gotOne = true;
									break;
								}
								else {
									loop++;
									roundRobin++;
									if(roundRobin >= currConnections) roundRobin=0;
								}
							}
						}
						while((gotOne==false)&&(loop < currConnections));
					}
					catch (SQLException e1) {}

					if(gotOne) {
						break;
					}
					else {
						synchronized(this) {  // Add new connections to the pool
							if(currConnections < maxConns) {
								try {
									createConn(currConnections);
													currConnections++;
								}
								catch(SQLException e) {
									log.println("Unable to create new connection: " + e);
								}
							}
						}

						try { Thread.sleep(2000); }
						catch(InterruptedException e) {}
						log.println("-----> Connections Exhausted!  Will wait and try " +
							"again in loop " +
							String.valueOf(outerloop));
					}
				} // End of try 10 times loop

			}
			else {
				log.println("Unsuccessful getConnection() request during destroy()");
			} // End if(available)

			return conn;
		}

		/**
		 * Returns the local JDBC ID for a connection.
		 */
		public int idOfConnection(Connection conn) {
			int match;
			String tag;

			try {
				tag = conn.toString();
			}
			catch (NullPointerException e1) {
				tag = "none";
			}

			match=-1;

			for(int i=0; i< currConnections; i++) {
				if(connID[i].equals(tag)) {
					match = i;
					break;
				}
			}
			return match;
		}

		/**
		 * Frees a connection.  Replaces connection back into the main pool for
		 * reuse.
		 */
		public String freeConnection(Connection conn) {
			String res="";

			int thisconn = idOfConnection(conn);
			if(thisconn >= 0) {
				connStatus[thisconn]=0;
				res = "freed " + conn.toString();
				//log.println("Freed connection " + String.valueOf(thisconn) +
				//            " normal exit: ");
			}
			else {
				log.println("----> Could not free connection!!!");
				System.out.println("cont ID:"+thisconn);
			}

			return res;
		}

		/**
		 * Returns the age of a connection -- the time since it was handed out to
		 * an application.
		 */
		public long getAge(Connection conn) { // Returns the age of the connection in millisec.
			int thisconn = idOfConnection(conn);
			return System.currentTimeMillis() - connLockTime[thisconn];
		}

		private void createConn(int i) throws SQLException {
			 Date now = new Date();
			 try {
				Class.forName (dbDriver);
				connPool[i] = DriverManager.getConnection
						  (dbConnectstring,dbLogin,dbPassword);
				connStatus[i]=0;
				connID[i]=connPool[i].toString();
				connLockTime[i]=0;
				connCreateDate[i] =  now.getTime();

				log.println(now.toString() + "  Opening connection " + String.valueOf(i) +
					" " + connPool[i].toString() + ":");
			}
			catch (ClassNotFoundException e2) {
				e2.printStackTrace();
				throw new SQLException(e2.getMessage());
			}
		}

		/**
		 * Shuts down the housekeeping thread and closes all connections
		 * in the pool. Call this method from the destroy() method of the servlet.
		 */

		/**
		 * Multi-phase shutdown.  having following sequence:
		 * <OL>
		 * <LI><code>getConnection()</code> will refuse to return connections.
		 * <LI>The housekeeping thread is shut down.<br>
		 *    Up to the time of <code>millis</code> milliseconds after shutdown of
		 *    the housekeeping thread, <code>freeConnection()</code> can still be
		 *    called to return used connections.
		 * <LI>After <code>millis</code> milliseconds after the shutdown of the
		 *    housekeeping thread, all connections in the pool are closed.
		 * <LI>If any connections were in use while being closed then a
		 *    <code>SQLException</code> is thrown.
		 * <LI>The log is closed.
		 * </OL><br>
		 * Call this method from a servlet destroy() method.
		 *
		 * @param      millis   the time to wait in milliseconds.
		 * @exception  SQLException if connections were in use after
		 * <code>millis</code>.
		 */
		public void destroy(int millis) throws SQLException {

			// Checking for invalid negative arguments is not necessary,
			// Thread.join() does this already in runner.join().

			// Stop issuing connections
			available=false;

			// Shut down the background housekeeping thread
			runner.interrupt();

			// Wait until the housekeeping thread has died.
			try { runner.join(millis); }
			catch(InterruptedException e){} // ignore

			// The housekeeping thread could still be running
			// (e.g. if millis is too small). This case is ignored.
			// At worst, this method will throw an exception with the
				// clear indication that the timeout was too short.

			long startTime=System.currentTimeMillis();

			// Wait for freeConnection() to return any connections
			// that are still used at this time.
			int useCount;
			while((useCount=getUseCount())>0 && System.currentTimeMillis() - startTime <=  millis) {
				try { Thread.sleep(500); }
				catch(InterruptedException e) {} // ignore
			}

			// Close all connections, whether safe or not
			for(int i=0; i < currConnections; i++) {
				try {
					connPool[i].close();
				}
				catch (SQLException e1)
				{
					log.println("Cannot close connections on Destroy");
				}
			}

			if(useCount > 0) {
				//bt-test successful
				String msg="Unsafe shutdown: Had to close "+useCount+
							" active DB connections after "+millis+"ms";
				log.println(msg);
				// Close all open files
				log.close();
				// Throwing following Exception is essential because servlet authors
				// are likely to have their own error logging requirements.
				throw new SQLException(msg);
			}

			// Close all open files
			log.close();

		}//End destroy()


		/**
		 * Less safe shutdown.  Uses default timeout value.
		 * This method simply calls the <code>destroy()</code> method
		 * with a <code>millis</code>
		 * value of 10000 (10 seconds) and ignores <code>SQLException</code>
		 * thrown by that method.
		 * @see     #destroy(int)
		 */
		public void destroy() {
			try {
				destroy(10000);
			}
			catch(SQLException e) {}
		}

		/**
		 * Returns the number of connections in use.
		 */
		// This method could be reduced to return a counter that is
		// maintained by all methods that update connStatus.
		// However, it is more efficient to do it this way because:
		// Updating the counter would put an additional burden on the most
		// frequently used methods; in comparison, this method is
		// rarely used (although essential).
		public int getUseCount() {
			int useCount=0;
			synchronized(connStatus) {
				for(int i=0; i < currConnections; i++) {
					if(connStatus[i] > 0) { // In use
						useCount++;
					}
				}
			}
			return useCount;
		}//End getUseCount()

		/**
		 * Returns the number of connections in the dynamic pool.
		 */
		public int getSize() {
			return currConnections;
		}//End getSize()
	}
	
	public class ConnectionWrapper implements Connection {

		private Connection connection;
		private ConnectionPool connectionPool;

		public ConnectionWrapper(Connection connection, ConnectionPool connectionPool) {
			this.connection = connection;
			this.connectionPool = connectionPool;
		}

		/**
		 * Instead of closing the underlying connection, we simply release
		 * it back into the pool.
		 */
		public int getHoldability() throws SQLException {
			return connection.getHoldability();
		}

		public int getTransactionIsolation() throws SQLException {
			return connection.getTransactionIsolation();
		}

		public void clearWarnings() throws SQLException {
			connection.clearWarnings();
		}
    
		/**
		 * Instead of closing the underlying connection, we simply release
		 * it back into the pool.
		 */
		public void close() throws SQLException {
			connectionPool.freeConnection(this.connection);
			//Release object references. Any further method calls on the
			//connection will fail.
			connection = null;
			connectionPool = null;
		}

		public void commit() throws SQLException {
			connection.commit();
		}

		public void rollback() throws SQLException {
			connection.rollback();
		}

		public boolean getAutoCommit() throws SQLException {
			return connection.getAutoCommit();
		}

		public boolean isClosed() throws SQLException {
			return connection.isClosed();
		}

		public boolean isReadOnly() throws SQLException {
			return connection.isReadOnly();
		}

		public void setHoldability(int arg0) throws SQLException {
			connection.setHoldability(arg0);
		}

		public void setTransactionIsolation(int level) throws SQLException {
			connection.setTransactionIsolation(level);

		}

		public void setAutoCommit(boolean autoCommit) throws SQLException {
			connection.setAutoCommit(autoCommit);
		}

		public void setReadOnly(boolean readOnly) throws SQLException {
			connection.setReadOnly(readOnly);
		}

		public String getCatalog() throws SQLException {
			return connection.getCatalog();
		}

		public void setCatalog(String catalog) throws SQLException {
			connection.setCatalog(catalog);
		}

		public DatabaseMetaData getMetaData() throws SQLException {
			return connection.getMetaData();
		}

		public SQLWarning getWarnings() throws SQLException {
			return connection.getWarnings();
		}

		public Savepoint setSavepoint() throws SQLException {
			return connection.setSavepoint();
		}

		public void releaseSavepoint(Savepoint arg0) throws SQLException {
			connection.releaseSavepoint(arg0);
		}

		public void rollback(Savepoint arg0) throws SQLException {
			connection.rollback(arg0);
		}

		public Statement createStatement() throws SQLException {
			return connection.createStatement();
		}

		public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
			return connection.createStatement(resultSetType, resultSetConcurrency);
		}

		public Statement createStatement(int arg0, int arg1, int arg2)
			throws SQLException {
			return connection.createStatement(arg0, arg1, arg2);
		}

		public Map getTypeMap() throws SQLException {
			return connection.getTypeMap();
		}

		public void setTypeMap(Map map) throws SQLException {
			connection.setTypeMap(map);
		}

		public String nativeSQL(String sql) throws SQLException {
			return connection.nativeSQL(sql);
		}

		public CallableStatement prepareCall(String sql) throws SQLException {
			return connection.prepareCall(sql);
		}

		public CallableStatement prepareCall(String sql, int resultSetType,
			int resultSetConcurrency) throws SQLException {
			return connection.prepareCall(sql, resultSetType, resultSetConcurrency);
		}

		public CallableStatement prepareCall(
			String arg0,
			int arg1,
			int arg2,
			int arg3)
			throws SQLException {
			return connection.prepareCall(arg0, arg1, arg2, arg3);
		}

		public PreparedStatement prepareStatement(String arg0)
			throws SQLException {
			return connection.prepareStatement(arg0);
		}

		public PreparedStatement prepareStatement(String arg0, int arg1)
			throws SQLException {
			return connection.prepareStatement(arg0, arg1);
		}

		public PreparedStatement prepareStatement(String arg0, int arg1, int arg2)
			throws SQLException {
			return connection.prepareStatement(arg0, arg1, arg2);
		}

		public PreparedStatement prepareStatement(
			String arg0,
			int arg1,
			int arg2,
			int arg3)
			throws SQLException {
			return connection.prepareStatement(arg0, arg1, arg2, arg3);
		}

		public PreparedStatement prepareStatement(String arg0, int[] arg1)
			throws SQLException {
			return connection.prepareStatement(arg0, arg1);
		}

		public Savepoint setSavepoint(String arg0) throws SQLException {
			return connection.setSavepoint(arg0);
		}

		public PreparedStatement prepareStatement(String arg0, String[] arg1)
			throws SQLException {
			return connection.prepareStatement(arg0, arg1);
		}
	}
}

